﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using JpLabs.Extensions;

namespace Duellum.Core
{
	public interface IDuelPlugin : IDisposable, ISupportInitialize
	{
		//public interface IDuelPluginPart
		//IEnumerable<IDuelPluginPart> GetParts();
		
		DuelDomain Domain { get; set; }
		
		//void BeginInit();
		//void EndInit();
		
		IEnumerable<DuelPluginName>		GetReferencedPlugins();
		
		IEnumerable<DuelType>		GetTypes();
		IEnumerable<DuelDecoration>	GetDecorations();

		//[Obsolete("This feature doesn't add any functionality that GetBaseTypes() doesn't cover")]
		//IEnumerable<DuelTransformation>	GetExtendedTypes();
	}
	
	[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
	public sealed class DuelPluginAttribute : Attribute
	{
		public string Name		{ get; set; }
		public Version Version	{ get; set; }
		public string Author	{ get; set; }
		
		public DuelPluginAttribute(string name)
		{
			Name = name;
		}
	}
	
	public class DuelPluginName : IEquatable<DuelPluginName>
	{
		public string Name					{ get; private set; }
		public Version Version				{ get; private set; }
		public string Author				{ get; private set; }
		internal Type PluginType			{ get; private set; }
		internal AssemblyName AssemblyName	{ get; private set; }
		
		private string key;
		
		private DuelPluginName() {}
		
		static public DuelPluginName FromType(Type type)
		{
			if (type.IsClass
			&&	typeof(IDuelPlugin).IsAssignableFrom(type)
			&&	!type.IsAbstract
			&&	type.IsDefined(typeof(DuelPluginAttribute), true)) {
				var attr = type.GetSingleAttr<DuelPluginAttribute>(false);
				var asm = type.Assembly;
				var asmName = asm.GetName();
				return new DuelPluginName()	{
					Name = attr.Name
					, Version = attr.Version ?? asmName.Version
					, Author = attr.Author ?? asm.GetSingleAttr<AssemblyCompanyAttribute>(false).Company
					, PluginType = type
					, AssemblyName = asmName
				};
			} else {
				return null;
			}
		}
		
		private IDuelPlugin CreateInstance(DuelDomain domain)
		{
			try {
				var plugin = (IDuelPlugin)Activator.CreateInstance(this.PluginType);
				
				plugin.Domain = domain;
				
				return plugin;
			} catch (TargetInvocationException e) {
				throw e.InnerException;
			}
		}
		
		internal DuelPluginProxy CreateProxy(DuelDomain domain)
		{
			var plugin = this.CreateInstance(domain);
			return DuelPluginProxy.Create(this, plugin);
		}
		
		private string Key
		{
			get {
				if (key == null) key = this.PluginType.AssemblyQualifiedName;
				return key;
			}
		}

		public override bool Equals(object obj)
		{
			if (obj is DuelPluginName) return this.Equals((DuelPluginName)obj);
			return false;
		}
		
		public bool Equals(DuelPluginName other)
		{
			return Key.Equals(other.Key);
		}

		public override int GetHashCode()
		{
			return Key.GetHashCode();
		}

		public override string ToString()
		{
			return string.Concat(Name, "; ", PluginType.ToString());
		}
	}
}
